package com.yc.sole.financial.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.yc.sole.erp.dto.EmployeeDto;
import com.yc.sole.erp.dto.OutputDto;
import com.yc.sole.erp.dto.ProcessDto;
import com.yc.sole.erp.dto.ShiftDto;
import com.yc.sole.erp.exception.ErpResultEnum;
import com.yc.sole.erp.repository.EmployeeRepository;
import com.yc.sole.erp.repository.OutputRepository;
import com.yc.sole.erp.repository.ProcessRepository;
import com.yc.sole.erp.repository.ShiftRepository;
import com.yc.sole.financial.dto.PieceRateWageDetailDto;
import com.yc.sole.financial.dto.PieceRateWageGroupDto;
import com.yc.sole.financial.exception.FinancialResultEnum;
import com.yc.sole.financial.repository.PieceRateWageDetailRepository;
import com.yc.sole.financial.repository.PieceRateWageGroupRepository;
import com.yc.sole.financial.request.PieceRateWageDetailQueryReq;
import com.yc.sole.financial.request.PieceRateWageDetailReq;
import com.yc.sole.financial.request.PieceRateWageGroupQueryReq;
import com.yc.sole.financial.response.PieceRateWageDetailResp;
import com.yc.sole.financial.response.PieceRateWageGroupResp;
import com.yc.sole.framework.exception.BaseException;
import com.yc.sole.framework.mybatis.page.Paging;
import com.yc.sole.framework.mybatis.util.PageUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务接口实现
 *
 * @author yizuomin
 * @date 2024-01-23
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class PieceRateWageBiz {

    private final PieceRateWageDetailRepository pieceRateWageRepository;
    private final ShiftRepository shiftRepository;
    private final OutputRepository outputRepository;
    private final EmployeeRepository employeeRepository;
    private final ProcessRepository processRepository;
    private final PieceRateWageGroupRepository pieceRateWageGroupRepository;

    /**
     * 修改
     *
     * @param pieceRateWageReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePieceRateWageDetailById(PieceRateWageDetailReq pieceRateWageReq) {
        PieceRateWageDetailDto dto = new PieceRateWageDetailDto();
        dto.setId(pieceRateWageReq.getId());
        dto.setDate(pieceRateWageReq.getDate());
        dto.setShiftId(pieceRateWageReq.getShiftId());
        dto.setSubsidy(pieceRateWageReq.getSubsidy());
        pieceRateWageRepository.updatePieceRateWageById(dto);
    }

    /**
     * 通过id批量删除
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void deletePieceRateWageDetailByIds(Collection<Long> ids) {
        pieceRateWageRepository.deletePieceRateWageByIds(ids);
    }

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    public PieceRateWageDetailResp selectPieceRateWageById(Long id) {
        PieceRateWageDetailResp pieceRateWageResp = BeanUtil.copyProperties(pieceRateWageRepository.selectPieceRateWageById(id), PieceRateWageDetailResp.class);
        if (Objects.isNull(pieceRateWageResp)) {
            return null;
        }
        EmployeeDto employeeDto = employeeRepository.selectEmployeeById(pieceRateWageResp.getEmployeeId());
        if (Objects.nonNull(employeeDto)) {
            pieceRateWageResp.setEmployeeName(employeeDto.getEmployeeName());
        }
        ShiftDto shiftDto = shiftRepository.selectShiftById(pieceRateWageResp.getShiftId());
        if (Objects.nonNull(shiftDto)) {
            pieceRateWageResp.setShiftName(shiftDto.getName());
        }
        ProcessDto processDto = processRepository.selectProcessById(pieceRateWageResp.getProcessId());
        if (Objects.nonNull(processDto)) {
            pieceRateWageResp.setProcessName(processDto.getName());
        }
        return pieceRateWageResp;
    }

    /**
     * 分页查询
     *
     * @param page
     * @param pieceRateWageQueryReq
     * @return
     */
    public Paging<PieceRateWageDetailResp> selectDetailPage(Paging<PieceRateWageDetailQueryReq> page, PieceRateWageDetailQueryReq pieceRateWageQueryReq) {
        Paging<PieceRateWageDetailDto> paging = pieceRateWageRepository.selectPieceRateWagePage(page, pieceRateWageQueryReq);
        Paging<PieceRateWageDetailResp> pieceRateWageRespPaging = PageUtil.pageCopy(paging, PieceRateWageDetailResp.class);
        if (CollUtil.isEmpty(pieceRateWageRespPaging.getRecords())) {
            return pieceRateWageRespPaging;
        }
        Set<Long> employeeIds = pieceRateWageRespPaging.getRecords().stream().map(PieceRateWageDetailResp::getEmployeeId).collect(Collectors.toSet());
        Set<Long> shiftIds = pieceRateWageRespPaging.getRecords().stream().map(PieceRateWageDetailResp::getShiftId).collect(Collectors.toSet());
        Set<Long> processIds = pieceRateWageRespPaging.getRecords().stream().map(PieceRateWageDetailResp::getProcessId).collect(Collectors.toSet());

        Map<Long, EmployeeDto> employeeDtoMap = employeeRepository.selectEmployeeByIds(employeeIds).stream().collect(Collectors.toMap(EmployeeDto::getId, v -> v));
        Map<Long, ShiftDto> shiftDtoMap = shiftRepository.selectShiftByIds(shiftIds).stream().collect(Collectors.toMap(ShiftDto::getId, v -> v));
        Map<Long, ProcessDto> processDtoMap = processRepository.selectProcessByIds(processIds).stream().collect(Collectors.toMap(ProcessDto::getId, v -> v));

        for (PieceRateWageDetailResp pieceRateWageResp : pieceRateWageRespPaging.getRecords()) {
            EmployeeDto employeeDto = employeeDtoMap.get(pieceRateWageResp.getEmployeeId());
            if (Objects.nonNull(employeeDto)) {
                pieceRateWageResp.setEmployeeName(employeeDto.getEmployeeName());
            }
            ShiftDto shiftDto = shiftDtoMap.get(pieceRateWageResp.getShiftId());
            if (Objects.nonNull(shiftDto)) {
                pieceRateWageResp.setShiftName(shiftDto.getName());
            }
            ProcessDto processDto = processDtoMap.get(pieceRateWageResp.getProcessId());
            if (Objects.nonNull(processDto)) {
                pieceRateWageResp.setProcessName(processDto.getName());
            }
        }
        return pieceRateWageRespPaging;
    }

    /**
     * 分页查询
     *
     * @param page
     * @param query
     * @return
     */
    public Paging<PieceRateWageGroupResp> selectGroupPage(Paging<PieceRateWageGroupQueryReq> page, PieceRateWageGroupQueryReq query) {
        Paging<PieceRateWageGroupResp> processRespPaging = PageUtil.pageCopy(pieceRateWageGroupRepository.selectPieceRateWageProcessPage(page, query), PieceRateWageGroupResp.class);
        if (CollUtil.isEmpty(processRespPaging.getRecords())) {
            return processRespPaging;
        }

        Set<Long> ids = processRespPaging.getRecords().stream().map(PieceRateWageGroupResp::getId).collect(Collectors.toSet());
        Set<Long> shiftIds = processRespPaging.getRecords().stream().map(PieceRateWageGroupResp::getShiftId).collect(Collectors.toSet());
        Set<Long> processIds = processRespPaging.getRecords().stream().map(PieceRateWageGroupResp::getProcessId).collect(Collectors.toSet());
        Map<Long, ShiftDto> shiftDtoMap = shiftRepository.selectShiftByIds(shiftIds).stream().collect(Collectors.toMap(ShiftDto::getId, v -> v));
        Map<Long, ProcessDto> processDtoMap = processRepository.selectProcessByIds(processIds).stream().collect(Collectors.toMap(ProcessDto::getId, v -> v));

        List<PieceRateWageDetailDto> pieceRateWageDtoList = pieceRateWageRepository.selectByGroupIds(ids);
        Map<Long, List<PieceRateWageDetailDto>> pieceRateWageMap = pieceRateWageDtoList.stream().collect(Collectors.groupingBy(PieceRateWageDetailDto::getGroupId));

        Set<Long> employeeIds = pieceRateWageDtoList.stream().map(PieceRateWageDetailDto::getEmployeeId).collect(Collectors.toSet());
        Map<Long, EmployeeDto> employeeDtoMap = employeeRepository.selectEmployeeByIds(employeeIds).stream().collect(Collectors.toMap(EmployeeDto::getId, v -> v));
        for (PieceRateWageGroupResp processResp : processRespPaging.getRecords()) {
            ShiftDto shiftDto = shiftDtoMap.get(processResp.getShiftId());
            if (Objects.nonNull(shiftDto)) {
                processResp.setShiftName(shiftDto.getName());
            }
            ProcessDto processDto = processDtoMap.get(processResp.getProcessId());
            if (Objects.nonNull(processDto)) {
                processResp.setProcessName(processDto.getName());
            }
            List<PieceRateWageDetailDto> wageDtoList = pieceRateWageMap.get(processResp.getId());
            if (CollUtil.isNotEmpty(wageDtoList)) {
                String employeeNames = wageDtoList.stream().map(pieceRateWageDto -> {
                    EmployeeDto employeeDto = employeeDtoMap.get(pieceRateWageDto.getEmployeeId());
                    if (Objects.nonNull(employeeDto)) {
                        return employeeDto.getEmployeeName();
                    }
                    return null;
                }).filter(Objects::nonNull).collect(Collectors.joining(","));
                processResp.setEmployeeNames(employeeNames);
            }
        }
        return processRespPaging;
    }

    /**
     * 计算工资
     *
     * @param pieceRateWageReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void calculatePieceRateWage(PieceRateWageDetailReq pieceRateWageReq) {
        // 查询班次
        ShiftDto shiftDto = shiftRepository.selectShiftById(pieceRateWageReq.getShiftId());
        if (Objects.isNull(shiftDto)) {
            throw new BaseException(ErpResultEnum.SHIFT_NOT_EXIST);
        }
        // 查询工序
        ProcessDto processDto = processRepository.selectProcessById(pieceRateWageReq.getProcessId());
        if (Objects.isNull(processDto)) {
            throw new BaseException(ErpResultEnum.PROCESS_NOT_FOUND);
        }
        // 查询当前班次产量
        List<OutputDto> outputDtoList = outputRepository.selectByShiftIdAndOutputDate(pieceRateWageReq.getShiftId(), pieceRateWageReq.getDate());
        // 总产量
        int outputQuantity = outputDtoList.stream().map(OutputDto::getQuantity).reduce(0, Integer::sum);
        if (outputQuantity <= 0) {
            throw new BaseException(FinancialResultEnum.OUTPUT_LESS_THAN_ZERO, pieceRateWageReq.getDate(), shiftDto.getName());
        }
        BigDecimal quantity = new BigDecimal(String.valueOf(outputQuantity));
        //查询员工
        List<EmployeeDto> employeeDtoList = employeeRepository.selectEmployeeByIds(pieceRateWageReq.getEmployeeIds());
        if (CollUtil.isEmpty(employeeDtoList)) {
            throw new BaseException(ErpResultEnum.EMPLOYEE_NOT_FOUND);
        }
        // 开始计算工资
        // 总员工数
        BigDecimal employeeCount = new BigDecimal(String.valueOf(employeeDtoList.size()));
        // 总计件工资
        BigDecimal totalPieceRateWage = quantity.multiply(processDto.getUnitPrice());
        BigDecimal pieceRateWage = totalPieceRateWage.divide(employeeCount, RoundingMode.HALF_UP);

        PieceRateWageGroupDto pieceRateWageProcessDto = new PieceRateWageGroupDto();
        pieceRateWageProcessDto.setDate(pieceRateWageReq.getDate());
        pieceRateWageProcessDto.setShiftId(pieceRateWageReq.getShiftId());
        pieceRateWageProcessDto.setProcessId(pieceRateWageReq.getProcessId());
        pieceRateWageProcessDto.setUnitPrice(processDto.getUnitPrice());
        pieceRateWageProcessDto.setQuantity(outputQuantity);
        pieceRateWageProcessDto.setPieceRateWage(pieceRateWage);
        pieceRateWageProcessDto.setTotalPieceRateWage(totalPieceRateWage);
        // 删除已计算的
        pieceRateWageGroupRepository.deleteByShiftIdProcessIdDate(pieceRateWageReq.getShiftId(), pieceRateWageReq.getProcessId(), pieceRateWageReq.getDate());
        // 保存新计算的
        Long id = pieceRateWageGroupRepository.savePieceRateWageProcess(pieceRateWageProcessDto);

        List<PieceRateWageDetailDto> pieceRateWageDtoList = employeeDtoList.stream().map(employeeDto -> {
            // 工资 = 总产量 * 工序单价 / 员工数
            PieceRateWageDetailDto pieceRateWageDto = new PieceRateWageDetailDto();
            pieceRateWageDto.setGroupId(id);
            pieceRateWageDto.setDate(pieceRateWageReq.getDate());
            pieceRateWageDto.setShiftId(pieceRateWageReq.getShiftId());
            pieceRateWageDto.setEmployeeId(employeeDto.getId());
            pieceRateWageDto.setProcessId(pieceRateWageReq.getProcessId());
            pieceRateWageDto.setPieceRateWage(pieceRateWage);
            pieceRateWageDto.setSubsidy(Objects.nonNull(pieceRateWageReq.getSubsidy()) ? pieceRateWageReq.getSubsidy() : BigDecimal.ZERO);
            pieceRateWageDto.setQuantity(outputQuantity);
            return pieceRateWageDto;
        }).collect(Collectors.toList());
        // 删除已计算的
        pieceRateWageRepository.deleteByShiftIdProcessIdDate(pieceRateWageReq.getShiftId(), pieceRateWageReq.getProcessId(), pieceRateWageReq.getDate());
        // 保存新计算的
        pieceRateWageRepository.savePieceRateWage(pieceRateWageDtoList);
    }

    /**
     * 批量删除分组
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void deletePieceRateWageGroup(Set<Long> ids) {
        // 删除详情
        pieceRateWageRepository.deleteByGroupIds(ids);
        // 删除
        pieceRateWageGroupRepository.deletePieceRateWageProcessByIds(ids);
    }
}
